package filtros;

/***********************************************************************************
FiltroMarr.java - Implementa o filtro de Marr e Hildreth para detec��o de bordas.

Direitos Autorais Reservados (c) 2008 Embrapa Inform�tica Agropecu�ria

Este programa � software livre; voc� pode redistribu�-lo e/ou modific�-lo sob os
termos da Licen�a P�blica Geral GNU conforme publicada pela Free Software
Foundation; tanto a vers�o 2 da Licen�a, como (a seu crit�rio) qualquer vers�o
posterior.

Este programa � distribu�do na expectativa de que seja �til, por�m, SEM NENHUMA
GARANTIA; nem mesmo a garantia impl�cita de COMERCIABILIDADE OU ADEQUA��O A UMA
FINALIDADE ESPEC�FICA. Consulte a Licen�a P�blica Geral do GNU para mais detalhes.

Voc� deve ter recebido uma c�pia da Licen�a P�blica Geral do GNU junto com este
programa; se n�o, escreva para a Free Software Foundation, Inc., no endere�o 59
Temple Street, Suite 330, Boston, MA 02111-1307 USA.

**********************************************************************************

Parte integrante do Comunicado T�cnico 090/08.
Acesso: <http://www.cnptia.embrapa.br/files/comtec90.pdf>

Descri��o:

	Implementa o filtro de detec��o de bordas de Marr e Hildreth.
 
	A implementa��o do filtro tem por refer�ncia o artigo:

	MARR, D.; HILDRETH, E. Theory of edge detection. Proceeding of the Royal 
	   Society of London. Series B, vol. 207, 187-217, 1980.

Par�metros de entrada:

  1. imagem =>	nome do arquivo imagem.
	2. dp			=>	desvio padr�o.

Sa�da:

  Painel com as bordas da imagem filtrada.

Desenvolvido por:

    Jos� Iguelmar Miranda & Jo�o Camargo Neto

Informa��es do CVS:
       $Source$:
       $Revision$:
       $Date$:

***********************************************************************************/

// pacote gen�rico
import java.io.File;

// pacotes do AWT
import java.awt.image.WritableRaster;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.GridLayout;

// pacote Swing para interface gr�fica
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.ImageIcon;

// leitura em modo imediato - para J2SE 1.4+
import javax.imageio.ImageIO;

public class FiltroMarr extends JFrame {

	// Define arrays para m�scaras direcionais
	int di[] = {0, -1, -1, -1, 0, 1, 1, 1};		// linhas
	int dj[] = {1, 1, 0, -1, -1, -1, 0, 1};		// colunas

  public static void main(String args[]) {

		// dp => desvio padr�o. Usar valor inicial > 1.3
		double dp = 1.0;

		// Certifica a passagem da imagem
    if (args.length != 2) {
			String msga = "Uso: java -cp . FiltroMarr <imagem> <dp>";
			String msgb = "\n   dp => desvio padrao.";
			System.out.println(msga+msgb);
      System.exit(0);
    }

		// Mostra JFrame decorado pelo Swing
		JFrame.setDefaultLookAndFeelDecorated(true);

    try {
        dp = Double.parseDouble(args[1]);
    } catch (NumberFormatException e) {
        System.out.println("Valor do parametro limiar invalido");
        System.exit(0);
    }

    // Chama o construtor passando o nome do arquivo imagem
		if (dp <= 0.0)	
			dp = 1.0;

		long eq_time = System.currentTimeMillis();

		FiltroMarr m = new FiltroMarr(args[0], dp); 

    eq_time = System.currentTimeMillis() - eq_time;
		String msg = "Marr-Hildreth: tempo de execucao ";
    System.out.println(msg + eq_time + " milisseg.");

		// Encerra a aplica��o clicando no "close"
		m.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		m.setVisible(true);
	}
  
	public FiltroMarr(String aFile, double dp) {
		
		// Define objeto BufferedImage para encapsular a imagem
    BufferedImage src = null, dest1 = null, dest2 = null;
		int w, h, tipo, pixel;
		JLabel img1, img2;
      
    // Certifica que a imagem existe
    File file = new File(aFile);
    try {
        src = ImageIO.read(file);
    } catch(Exception e) {
        System.out.println("Imagem '" + aFile + "' nao existe.");
        System.exit(0);
    }

    // Atribui nome ao frame
    setTitle("Marr-Hildreth: " + file.getName());

		w = src.getWidth();
		h = src.getHeight();
    Raster srcR = src.getRaster(); 
    tipo = BufferedImage.TYPE_BYTE_GRAY;
    dest1 = new BufferedImage(w, h, tipo);
    dest2 = new BufferedImage(w, h, tipo);
    WritableRaster dest1WR = dest1.getRaster();
    WritableRaster dest2WR = dest2.getRaster();

		// Copia imagem original
		for (int i = 0; i < h; i++)
			for (int j = 0; j < w; j++) {
				pixel = srcR.getSample(j, i, 0);
				dest1WR.setSample(j, i, 0, pixel);
				dest2WR.setSample(j, i, 0, pixel);
			}

		marrOp(dp - 0.8, dest1);
		marrOp(dp + 0.8, dest2);

    Raster dest1R = dest1.getRaster(); 
    Raster dest2R = dest2.getRaster(); 
		for (int i = 0; i < h; i++)
			for (int j = 0; j < w; j++)
				if (dest1R.getSample(j, i, 0) > 0 && 
						dest2R.getSample(j, i, 0) > 0)
					dest1WR.setSample(j, i, 0, 0);
				else
					dest1WR.setSample(j, i, 0, 255);

		// Cria gridLayout de 1 x 2
    getContentPane().setLayout(new GridLayout(1, 2));
		img1 = new JLabel(new ImageIcon(src));
		img2 = new JLabel(new ImageIcon(dest1));
		setSize(2*w, h);
		getContentPane().add(new JScrollPane(img1));
		getContentPane().add(new JScrollPane(img2));
	}

	// Usa o filtro de Marr e Hildreth
	// s = desvio padr�o
	public void marrOp(double s, BufferedImage src) {
		int width, k, n, nLin, nCol;
		double[][]	amostraLoG, // amostra bidimensional do Laplaciano do Gaussiano
								arrayLaplace;

		nCol = src.getWidth();
		nLin = src.getHeight();

		WritableRaster srcWR = src.getRaster();

		// Cria uma Gaussiana e um filtro com a derivada da Gaussiana 
		width = (int)(3.35*s + 0.33);
		n = 2*width + 1;
		System.out.println("Suavizando com uma Gaussiana de tamanho " + n + 
											 "x" + n + "\n");
		amostraLoG = new double[n][n];
		for (int i = 0; i < n; i++)
			for (int j = 0; j < n; j++)
				amostraLoG[j][i] = funcLoG(distancia((double)i, (double)j,
										 (double)width, (double)width), s);

		// Convolu��o da imagem fonte com a Gaussiana nas dire��es X e Y
		System.out.println("Convolucao com Laplaciano do Gaussiano:\n");
		arrayLaplace = convolveImagem(src, amostraLoG);

		// Localiza os cruzamentos zeros
		System.out.println("Cruzamentos zero:\n");
		cruzaZero(arrayLaplace, src);

		// Limpa as bordas
		for (int i = 0; i < nLin; i++) {
			for (int j = 0; j <= width; j++)
				srcWR.setSample(j, i, 0, 0);
			for (int j = nCol - width - 1; j < nCol; j++)
				srcWR.setSample(j, i, 0, 0);
		}
		for (int j = 0; j < nCol; j++) {
		  for (int i = 0; i <= width; i++)
				srcWR.setSample(j, i, 0, 0);
			for (int i = nLin - width - 1; i < nLin; i++)
				srcWR.setSample(j, i, 0, 0);
		}
	}

	private double distancia(double a, double b, double c, double d) {
		return Math.sqrt((a-c)*(a-c) + (b-d)*(b-d));
	}

	// Implementa��o da equa��o (5) - Laplaciano do Gaussiano
	private double funcLoG(double r, double sigma) {
		double x1;

		x1 = gauss(r, sigma);
		return (r*r-2*sigma*sigma)/(sigma*sigma*sigma*sigma) * x1;
	}

	// Implementa��o da equa��o (5) - Gaussiana
	private double gauss(double r, double sigma) {
    return Math.exp(((-r*r)/(2*sigma*sigma)));
	}

	private double[][] convolveImagem(BufferedImage im, double[][] amostraLG) { 

		int n, m, nLin2, nCol2, nLin, nCol, imCol, imLin;
		double x, res[][];

		imCol = im.getWidth();
		imLin = im.getHeight();
		nCol = nLin = amostraLG.length;

    Raster imR = im.getRaster(); 

		res = new double[imCol][imLin];
		nLin2 = (int)nLin/2;
		nCol2 = (int)nCol/2;
		for (int i = 0; i < imLin; i++)
			for (int j = 0; j < imCol; j++) {
				x = 0.0;
				for (int ii = 0; ii < nLin; ii++) {
					n = i - nLin2 + ii;
					if (n < 0 || n >= imLin)
						continue;
					for (int jj = 0; jj < nCol; jj++) {
						m = j - nCol2 + jj;
						if ( m < 0 || m >= imCol)
							continue;
						x += amostraLG[jj][ii] * (double)imR.getSample(m, n, 0);
					}
				}
				res[j][i] = x;
			}
			return res;
	}

	public void cruzaZero(double[][] imgLaplaciano, BufferedImage im) {

		int w, h;

		w = im.getWidth();
		h = im.getHeight();
    WritableRaster imWR = im.getRaster();

		for (int i = 1; i < h - 1; i++)
			for (int j = 1; j < w - 1; j++) {
		    imWR.setSample(j, i, 0, 0);
		    if (imgLaplaciano[j][i-1]*imgLaplaciano[j][i+1] < 0) {
					imWR.setSample(j, i, 0, 255);
					continue;
				}
		    if (imgLaplaciano[j-1][i]*imgLaplaciano[j+1][i] < 0) {
					imWR.setSample(j, i, 0, 255);
					continue;
				}
		    if (imgLaplaciano[j-1][i+1]*imgLaplaciano[j+1][i-1] < 0) {
					imWR.setSample(j, i, 0, 255);
					continue;
				}
		    if (imgLaplaciano[j-1][i-1]*imgLaplaciano[j+1][i+1] < 0) {
					imWR.setSample(j, i, 0, 255);
					continue;
				}
			}
	}
}
